#include <stdio.h>
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>

#define BUF_SIZE 64
#define NAME_SIZE 64
#define NO -1
#define MSG_BODY_SIZE 4

typedef struct filenode{
    char name[NAME_SIZE];//文件名
    int offset;//文件偏移量
    int buffsize;//单次最大信息长度
}filenode,*filelist;

typedef struct my_msg_body{
    int size;//有效信息长度
    char buf[BUF_SIZE];
}my_msg_body,*my_msg_body_list;

typedef struct my_msg{
    int size;//消息体长度
    my_msg_body node;
}my_msg,*my_msg_list;

void my_close(int sockfd,int fd_r){

    if(sockfd > 0){
        close(sockfd);
    }
    
    if(fd_r > 0){
        close(fd_r);
    }

    return;
}

int my_recv(int sockfd,void* buf,int length){

    int temp=0;
    int ftemp=0;

    while(1){
        
        ftemp=recv(sockfd,buf+temp,length - temp,0);//0 阻塞(发送的缓冲区满/网络阻塞);
        if(ftemp == -1){
            perror("my_send err");
            return -1;
        }
        temp+=ftemp;
        if(ftemp == 0){
            return temp;
        }
        if(temp >= length){
            break;
        }
        
    }
    return temp;
}

int my_send(int sockfd,const void* buf,int length){

    int temp=0;
    int ftemp=0;

    while(1){
        
        ftemp=send(sockfd,buf+temp,length - temp,0);//0 阻塞(发送的缓冲区满/网络阻塞);
        if(ftemp == -1){
            perror("my_send err");
            return -1;
        }
        temp+=ftemp;
        if(temp >= length){
            break;
        }
        
    }
    return 0;
}

int file_size(char* filename){
    int temp =0;
    if(access(filename,F_OK) == 0){
        int fd_r = open(filename,O_RDONLY);
        if(fd_r == 0){
            perror("open err");
            return -1;
        }
        temp=lseek(fd_r,0,SEEK_END);

        close(fd_r);
    }else{
        return 0;
    }
    

    return temp;
}

int send_file_msg(int sockfd,filelist stat,int* size_file,int statsize,const char* name){//获取文件信息，告知单次最大传输大小
    memset(stat,0,statsize);
    strcpy(stat->name,name);
    stat->offset=file_size(stat->name);
    printf("%d\n",stat->offset);
    stat->buffsize=htonl(BUF_SIZE);
    if(stat->offset < 0){
        perror("read size err");
        my_close(sockfd,NO);
        return -1;
    }else{
        stat->offset=htonl(stat->offset);
    }

    if(my_send(sockfd,stat,statsize) == -1){ //将文件名，和已下载的字节数传到服务端
        perror("send err");
        my_close(sockfd,NO);
        return -1;
    }
    if(my_recv(sockfd,size_file,sizeof(int)) < 0){
        my_close(sockfd,NO);
        return -1;
    }
    printf("%d\n",ntohl(*size_file));
    stat->offset=ntohl(stat->offset);
    *size_file=ntohl(*size_file) - stat->offset;
    if(*size_file == 0){
        printf("recv complete\n");
        my_close(sockfd,NO);
        return -1;
    }
}

int my_recv_serialization(int sockfd,filelist stat,int size_file,my_msg_body_list node,my_msg_list bodynode){//反序列
    int fd_w = open(stat->name,O_WRONLY | O_CREAT,0666);
    if(fd_w == -1){
        perror("open err");
        my_close(sockfd,NO);
        return -1;
    }
    lseek(fd_w,stat->offset,SEEK_SET);
    int ret=0;
    while(1){
        if(my_recv(sockfd,&(node->size),MSG_BODY_SIZE) == MSG_BODY_SIZE){//获取单次有效信息长度
            node->size=ntohl(node->size);
            
            memset(node->buf,0,bodynode->size-MSG_BODY_SIZE);
            ret =my_recv(sockfd,node->buf,bodynode->size-MSG_BODY_SIZE);//获取信息
            if(ret < 0){
                perror("recv err");
                continue;
            }else if(ret >= 0){
                
                size_file-=ret;
                // printf("nodesize:%d  ret:%d  size_file:%d\n",node->size,ret,size_file);
                if(write(fd_w,node->buf,ret) == -1){
                    break;
                }
                if(size_file == 0){//全部数据接收完毕
                    printf("recv complete\n");
                    break;
                }
                if(ret == 0){
                    printf("sever Disconnect\n");
                    break;
                }

            }

        }else{
            close(fd_w);
            return -1;
        }
    }
    
    
    close(fd_w);
    return 0;
}

int my_recv_Sticky_packs(int sockfd,filelist stat,int size_file,my_msg_list bodynode){

    int nodesize=sizeof(bodynode->node);
    if(my_recv(sockfd,&(bodynode->size),sizeof(int)) < 0){//收消息头
        my_close(sockfd,NO);
        return -1;
    }
    bodynode->size=ntohl(bodynode->size);
    if(bodynode->size > nodesize){//如果sever发送的消息体大于client的消息体
        return -1;
    }
    if(my_recv_serialization(sockfd,stat,size_file,&(bodynode->node),bodynode) == -1){
        close(sockfd);
        return -1;
    }
    return 0;
}

int main(int argc,char const *argv[]){
    if(argc < 2){
        printf("usage err\n");
        return -1;
    }
    int sockfd = socket(AF_INET,SOCK_STREAM,0);//ipv4,tcp
    if(sockfd == -1){
        perror("sock err");
        return -1;
    }
    //服务器地址:IP 端口号
    struct sockaddr_in server_addr;
    server_addr.sin_family = AF_INET;
    // server_addr.sin_port =htons(atoi(argv[2]));
    server_addr.sin_port =htons(8888);

    // server_addr.sin_addr.s_addr=inet_addr(argv[1]);
    server_addr.sin_addr.s_addr=inet_addr("127.0.0.1");

    printf("IP=%s\n",inet_ntoa(server_addr.sin_addr));
    socklen_t addrlen= sizeof(server_addr);
    

    //连接
    if(connect(sockfd,(struct sockaddr*)&server_addr,addrlen) < 0){
        perror("coonet err");
        close(sockfd);
        return -1;
    }
    printf("connet  success\n");
    //文件信息
    
    filenode stat;
    int size_file=0;
    if(send_file_msg(sockfd,&stat,&size_file,sizeof(stat),argv[1]) == -1){
        return -1;
    }
    

   //收发数据
   
    my_msg bodynode;
    if(my_recv_Sticky_packs(sockfd,&stat,size_file,&bodynode) == -1){
        close(sockfd);
        return -1;
    }
    


    //关闭
    my_close(sockfd,NO);


    return 0;
}
